package projectpackage;

import java.io.*;
import java.net.Socket;

import javax.net.ssl.*;
import java.security.*;
import java.util.Observable;

public class Server extends Observable implements Runnable {
	static final int port = 8189;
	static final String KEYSTORE = "keystores/projectkeystore.ks";
	static final String TRUSTSTORE = "keystores/projecttnm031truststore.ks";
	static final String STOREPASSWD = "abcabc";
	static final String ALIASPASSWD = "abcabc";
	
	//Alias
	static final String ALIASTRUST = "projecttnm031cert";
	static final String ALIASKEY = "tnm031pair";
	
	//The server
	protected boolean isRunning;
	private Serverstate serverstate;
	private static Serverinterface frame;	
	
	// This chat server can accept up to maxClientsCount clients' connections.
	private static final int maxClientsCount = 3;
	private static final Clientworker[] threads = new Clientworker[maxClientsCount];	
	private static final Socket[] sockets = new Socket[maxClientsCount];
	private boolean Alreadyexists = false;
	
	public Server()	{
		isRunning = true;	
	}
	
	public Serverinterface getframe() {
		return Server.frame;
	}
	
	public void run() {		
		try {
			serverstate = new Serverstate(sockets, "Starting server.... \n");
			serverstate.Changeprocent(0);
			this.setChanged();
			this.notifyObservers(serverstate);
			
			KeyStore ks = KeyStore.getInstance( "JCEKS" );
			ks.load( new FileInputStream( KEYSTORE ), STOREPASSWD.toCharArray() );			
			serverstate.ChangeConsoleState("The algorithm for alias is :" + ks.getKey(ALIASKEY, ALIASPASSWD.toCharArray()).getAlgorithm() + "\n");		
			this.setChanged();
			this.notifyObservers(serverstate);
			
			//Certificate
			KeyStore ts = KeyStore.getInstance( "JCEKS" );
			ts.load( new FileInputStream( TRUSTSTORE ), STOREPASSWD.toCharArray() );
			serverstate.Changeprocent(25);
			serverstate.ChangeConsoleState("Certificate type from alias :" + ts.getCertificate( ALIASTRUST ).getType() + "\n");		
			this.setChanged();
			this.notifyObservers(serverstate);	
			
			// KeyManager's decide which key material to use.
			KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
			kmf.init( ks, ALIASPASSWD.toCharArray() );
			
			// TrustManager's decide whether to allow connections.
			TrustManagerFactory tmf = TrustManagerFactory.getInstance( "SunX509" );
			tmf.init( ts );
			
			SSLContext sslContext = SSLContext.getInstance( "TLS" );
			sslContext.init( kmf.getKeyManagers(), tmf.getTrustManagers(), null );
			SSLServerSocketFactory sslServerFactory = sslContext.getServerSocketFactory();
			
			SSLServerSocket server = (SSLServerSocket) sslServerFactory.createServerSocket( port );
			String[] ciphers = server.getSupportedCipherSuites();
			String[] selectedCiphers = { ciphers[9] };						
			server.setEnabledCipherSuites( selectedCiphers );
			serverstate.Changeprocent(100);
			serverstate.ChangeConsoleState("Cipher is "+ciphers[9] + "\n" + "Server adress : " + server.getInetAddress() +"\n" + "Server port " + server.getLocalPort() + "\n");		
			this.setChanged();
			this.notifyObservers(serverstate);
			
			//Create a socket
			while(isRunning)
			{				
				Socket clientSocket = null;					
				clientSocket = server.accept();
				Alreadyexists = false;
				int i = 0;
				
				for (int check = 0; check < maxClientsCount; check++) {
					if(sockets[check] != null)
					{
						if(sockets[check].getInetAddress().equals(clientSocket.getInetAddress())) {
							Alreadyexists = true;							
						}
					}					
				}
				if(!Alreadyexists)
				{		
					
					for (i = 0; i < maxClientsCount; i++) {
						if (threads[i] == null) {
							sockets[i] = clientSocket;
							(threads[i] = new Clientworker(clientSocket, threads,sockets,frame)).start();            
							serverstate.ChangeClientAmount(i+1);
							serverstate.ChangeConsoleState(clientSocket.getInetAddress().toString()+" is connected to the server \n");											
							this.setChanged();
							this.notifyObservers(serverstate);	
							break;
						}			        
					}
				}
			    if (i == maxClientsCount) {
			        PrintStream os = new PrintStream(clientSocket.getOutputStream());
			        serverstate.ChangeConsoleState(clientSocket.getInetAddress().toString()+"cannot connect to server."+ "\n"+ "No threads available \n");
					serverstate.ChangeVectorState(sockets);	
					this.setChanged();
					this.notifyObservers(serverstate);
			        os.close();
			        clientSocket.close();
			    }		    			
			}
			if(!isRunning)
			{
				serverstate.ChangeConsoleState("Clients cannot connect to server.....");
				this.setChanged();
				this.notifyObservers(serverstate);
			}
		}
		catch( Exception x ) {
			System.out.println( x );
			x.printStackTrace();
		}
	}
	
	void destroy() {
		isRunning = false;
	}
	
	public static void main( String[] args ) {	
		try{
			frame = new Serverinterface();
			frame.setVisible(true);	
		}catch( Exception x ) {
			System.out.println("Didnt open the frame window" + x );
			x.printStackTrace();
		}
	}
}